package com.example.airhockey3d.helper

import android.opengl.GLES20.*
import android.util.Log

object ShaderHelper {
    private const val TAG = "ShaderHelper"

    /**
     * 创建顶点着色器
     * */
    fun compileVertexShader(shaderCode: String): Int {
        return compileShader(GL_VERTEX_SHADER, shaderCode)
    }

    /**
     * 创建片段着色器
     * */
    fun compileFragmentShader(shaderCoder: String): Int {
        return compileShader(GL_FRAGMENT_SHADER, shaderCoder)
    }

    /**
     * 创建着色器
     * */
    fun compileShader(type: Int, shaderCoder: String): Int {
        //创建一个新的着色器对象，并将这个对象的id存入shaderObjectId
        val shaderObjectId = glCreateShader(type)
        //判断是否这个着色器对象是否有效，openGl 不会抛出异常 所以我们需要返回0来通过glGetError来告诉我们这个错误
        if (shaderObjectId == 0) {
            Log.d(TAG, "could not create new shader")
            return 0
        }
        //上传着色器源码到有效的着色器对象
        glShaderSource(shaderObjectId, shaderCoder)
        //上传后就可以编译着色器了
        glCompileShader(shaderObjectId)

        //获取编译状态，在openGl中 通常会定义一个长度为1 的数组，将结果存进这个数组第一个元素中
        val compileStatus = IntArray(1)
        glGetShaderiv(shaderObjectId, GL_COMPILE_STATUS, compileStatus, 0)
        Log.d(
            TAG,
            "Results of compile source : \n $shaderCoder  \n ${glGetShaderInfoLog(shaderObjectId)}"
        )
        if (compileStatus[0] == 0) {
            Log.d(TAG, "Compilation of shader failed")
            glDeleteShader(shaderObjectId)
            return 0
        }

        return shaderObjectId
    }

    /**
     * 创建链接程序
     * */
    fun linkProgram(vertexShapeId: Int, fragmentShaderId: Int): Int {
        //创建一个程序对象
        val programObjectId = glCreateProgram()

        //验证是否创建成功 为0 代表不成功
        if (programObjectId == 0) {
            Log.w(TAG, "could not create new program")
            return 0
        }
        //附加顶点着色器到刚刚创建程序上面
        glAttachShader(programObjectId, vertexShapeId)
        //附加片段着色器到刚刚创建的程序上面
        glAttachShader(programObjectId, fragmentShaderId)
        //将着色器链接起来
        glLinkProgram(programObjectId)

        //创建一个数组来容纳检测结果
        val linkStatus = IntArray(1)
        //调用检测 将结果放入刚刚创建的整形数组中
        glGetProgramiv(programObjectId, GL_LINK_STATUS, linkStatus, 0)
        Log.d(
            TAG,
            "Results of linking program:  \n ${glGetProgramInfoLog(programObjectId)}"
        )
        //等于0代表这个程序出错 删除这个程序，
        if (linkStatus[0] == 0) {
            Log.d(TAG, "Compilation of shader failed")
            glDeleteProgram(programObjectId)
            return 0
        }

        return programObjectId
    }

    /**
     * 验证程序
     * */
    fun validateProgram(programObjectId: Int): Boolean {
        glValidateProgram(programObjectId)

        val validateStatus = IntArray(1)
        glGetProgramiv(programObjectId, GL_VALIDATE_STATUS, validateStatus, 0)
        Log.v(
            TAG,
            "Results of validating program :  ${validateStatus[0]}  \n log  : ${glGetProgramInfoLog(
                programObjectId
            )}"
        )
        return validateStatus[0] != 0
    }
}